package com.abl.service.system.impl;

import com.abl.common.cache.CacheService;
import com.abl.common.mybatis.dao.Dao;
import com.abl.common.mybatis.model.Dnt;
import com.abl.common.mybatis.model.Page;
import com.abl.common.mybatis.strengthen.Wrapper;
import com.abl.domain.po.Table;
import com.abl.domain.dto.system.UserAddDTO;
import com.abl.domain.model.AblError;
import com.abl.domain.model.Param;
import com.abl.common.util.DateKit;
import com.abl.common.util.JwtKit;
import com.abl.common.util.MathKit;
import com.abl.common.util.StringKit;
import com.abl.common.util.coll.ArrayKit;
import com.abl.service.system.SysService;
import com.abl.service.system.SysUserService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 用户表实现
 */
@Slf4j
@Service
public class SysUserServiceImpl implements SysUserService {
    @Autowired
    private Dao dao;

    @Autowired
    private SysService system;

    @Autowired
    private CacheService cache;

    //新增
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(UserAddDTO param2) {
        JSONObject param = JSON.parseObject(JSON.toJSONString(param2));
        //账号、手机号、邮箱
        String account = param.getString("account");
        String phone = param.getString("phone");
        String email = param.getString("email");

        //查询账号重复
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.eq("d_s", "0");
        wrapper.eq("account", account);
        Dnt sys_user = dao.selectOne("sys_user", wrapper);
        if (sys_user != null) {
            throw new AblError("添加失败：账号【" + account + "】已经存在！");
        }

        //查询手机号重复
        if (StringKit.isNotBlank(phone)) {
            wrapper = new Wrapper<>();
            wrapper.eq("d_s", "0");
            wrapper.ne("account", account);
            wrapper.eq("phone", phone);
            wrapper.select("account");
            Dnt sys_user1 = dao.selectOne("sys_user", wrapper);
            if (sys_user1 != null) {
                throw new AblError("添加失败：手机号【" + phone + "】 已被 【" + sys_user1.getString("account") + "】绑定！");
            }
        }

        //查询邮箱号重复
        if (StringKit.isNotBlank(phone)) {
            wrapper = new Wrapper<>();
            wrapper.eq("d_s", "0");
            wrapper.ne("account", account);
            wrapper.eq("email", email);
            wrapper.select("account");
            Dnt sys_user1 = dao.selectOne("sys_user", wrapper);
            if (sys_user1 != null) {
                throw new AblError("添加失败：邮箱【" + email + "】 已被 【" + sys_user1.getString("account") + "】绑定！");
            }
        }

        //配置密码
        String password = param.getString("password");
        //默认密码
        if (StringKit.isBlank(password)) {
            param.put("password", defaultPassword(account));
        }
        //参数密码
        else {
            String ap = MathKit.getMD5(account + password);
            String salt = system.dictValue("user_salt", "gK3fQmj2tmWk3ScaW1jBch6svMe1CatO");
            param.put("password", MathKit.getMD5(ap + salt));
        }

        //添加用户
        Dnt user = Dnt.createInsert(param);
        Object name = param.getOrDefault("name", MathKit.getMD5(UUID.randomUUID().toString()));
        user.set("name", name);
        user.set("user_img", system.dictValue("user", "default_img"));
        user.remove("role");
        dao.insert("sys_user", user);

        //添加附属信息
        Dnt userProp = Dnt.create();
        userProp.set("id", user.getString("id"));
        userProp.set("card", MathKit.getMD5(UUID.randomUUID().toString()));
        dao.insert("sys_user_prop", userProp);

        //更新用户角色
        JSONArray roleIds = param.getJSONArray("roleIds");
        updateLink(user.getString("id"), roleIds);

        return true;
    }

    //登录
    @Override
    public Dnt login(Param<JSONObject> param) {
        //账号、密码
        //前端传来的 password = MD5(账号+明文密码) 取MD5大写
        String account = param.getString("account", " ");
        String type = param.getString("type", "0");

        //查用户信息
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.eq("d_s", 0);
        switch (type) {
            //根据账号查找
            case "0":
                wrapper.eq("account", account);
                break;
            //根据电话查找
            case "1":
                wrapper.eq("phone", account);
                break;
            //根据邮箱查找
            case "2":
                wrapper.eq("email", account);
                break;
            //参数错误
            default:
                throw new AblError("登录失败：参数错误！");
        }
        Dnt user = dao.selectOne("sys_user", wrapper);
        if (user == null) {
            throw new AblError("登录失败：用户信息不存在！");
        }

        //检查错误登录次数
        int login_error_count = user.getInteger("login_error_count");
        int login_error_max_count = Integer.parseInt(system.dictValue("login_error_max_count", "10"));
        if (login_error_count > login_error_max_count) {
            throw new AblError("登录失败：连续错误登录，超过" + login_error_max_count + "次，账户已锁定，请联系管理员处理。");
        }

        //校验密码
        String md5Password = param.getString("password", " ");
        String salt = system.dictValue("user_salt", "gK3fQmj2tmWk3ScaW1jBch6svMe1CatO");
        String frontPwd = MathKit.getMD5(md5Password + salt);
        String userPwd = user.getString("password");
        //登录失败
        if (!frontPwd.equals(userPwd)) {
            //登录失败次数加 1
            wrapper = new Wrapper<>();
            wrapper.eq("account", account);
            wrapper.set("login_error_count", login_error_count + 1);
            dao.update("sys_user", wrapper);
            throw new AblError("登录失败：密码错误！");
        }

        //TODO 其他检查
        //其他检查（异常登录次数，异地登录，账号状态异常.....）
        //其他更新（登录地址，上次登录地址，登录时间，登录日志.....）


        //登录成功
        String userId = user.getString("id");

        //更新登录信息
        String refreshToken = MathKit.getMD5(System.currentTimeMillis() + "");
        Dnt update = Dnt.create();
        update.set("id", userId);
        update.set("d_ui", userId);
        update.set("d_ut", DateKit.dateToStr(new Date()));
        update.set("refresh_token", refreshToken);
        update.set("login_error_count", 0);
        dao.updateById("sys_user", update);

        //签发Token
        HashMap<Object, Object> tokenParam = new HashMap<>();
        tokenParam.put("userId", userId);
        tokenParam.put("roleIds", userRoleIds(userId));
        tokenParam.put("userAccount", user.getString("account"));
        tokenParam.put("userName", user.getString("name"));
        String token = JwtKit.createToken(tokenParam, Integer.parseInt(system.dictValue("login_time_out", "2")));

        //
        Dnt data = Dnt.create();
        data.set("token", token);
        data.set("refreshToken", refreshToken);

        return data;
    }

    //退出登录状态
    //TODO 单点登录和单机登录
    @Override
    public boolean logout(Param<JSONObject> param) {
        cache.delete("login_user_" + loginUserId());
        return true;
    }


    //删除
    @Override
    public long del(Param<JSONObject> param) {
        JSONArray ids = param.getJSONArray("ids");
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.set("d_s", 1);
        wrapper.in("id", ids);

        return dao.update("sys_user", wrapper);
    }

    //修改
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean edit(Param<JSONObject> param) {
        //修改账号信息
        JSONObject user = param.getJSONObject("user");
        Dnt update = Dnt.createUpdate();
        update.set("id", user.getString("id"));
        update.set("phone", user.getString("phone"));
        update.set("email", user.getString("email"));

        //更新用户角色
        JSONArray roleIds = param.getJSONArray("roleIds");
        updateLink(user.getString("id"), roleIds);

        dao.updateById("sys_user", update);

        //修改用户信息
        JSONObject userProp = param.getJSONObject("userProp");
        Dnt update1 = Dnt.createUpdate();
        update1.set(userProp);
        dao.updateById("sys_user_prop", update1);

        return true;
    }

    public void updateLink(String userId, JSONArray roleIds) {
        Wrapper<Object> wrapper = new Wrapper<>();
        if (ArrayKit.isNotBlank(roleIds)) {
            //删除关联关系
            wrapper.in("user_id", userId);
            dao.delete(Table.sys_link_user_role, wrapper);

            //批量插入
            ArrayList<Dnt> links = new ArrayList<>();
            for (var role_id : roleIds) {
                Dnt dnt = Dnt.create();
                dnt.set("role_id", role_id);
                dnt.set("user_id", userId);
                links.add(dnt);
            }
            dao.insertBatch(Table.sys_link_user_role, links);
        }
    }

    //查询详情
    @Override
    public Dnt info(Param<JSONObject> param) {
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.eq("d_s", "0");
        wrapper.eq("id", param.getOrDefault("id", 0));

        return dao.selectOne("sys_user", wrapper);
    }

    //查询集合
    @Override
    public List<Dnt> list(Param<JSONObject> param) {
        //账号、昵称、手机号
        String account = param.getString("account", "");
        String name = param.getString("name", "");
        String phone = param.getString("phone", "");
        //组织
        String orgId = param.getString("orgId");
        //角色、职位
        var roleIds = param.getList("roleIds");
        var postIds = param.getList("postIds");

        //全条件查询主表
        Wrapper<Object> qw = new Wrapper<>();
        qw.select("""
                SELECT
                	 usr.*
                FROM
                	sys_user usr
                	LEFT JOIN sys_link_user_org lo ON lo.user_id = usr.id
                	LEFT JOIN sys_link_user_role lr ON lr.user_id = usr.id
                	LEFT JOIN sys_link_user_post lp ON lp.user_id = usr.id
                """);
        qw.eq("usr.d_s", 0);
        qw.eq(StringKit.isNotBlank(param.getString("id")), "usr.id", param.getString("id"));
        qw.like(StringKit.isNotBlank(account), "usr.account", account);
        qw.like(StringKit.isNotBlank(name), "usr.name", name);
        qw.like(StringKit.isNotBlank(phone), "usr.phone", phone);
        //组织
        qw.eq(StringKit.isNotBlank(orgId), "lo.org_id", orgId);
        //角色、职位
        qw.in(ArrayKit.geZero(roleIds), "lr.role_id", roleIds);
        qw.in(ArrayKit.geZero(postIds), "lp.post_id", postIds);
        qw.groupBy("usr.id");
        qw.orderByAsc("usr.sort");


        List<Dnt> data;
        Page<Dnt> dntPage = new Page<>();
        if (param.isPage()) {
            dntPage = dao.page(qw, param.pageCurrent(), param.pageSize());
            data = dntPage.getData();
        } else {
            data = dao.list(qw);
        }
        //根据主表数据查子表信息
        //Set<String> userIds = data.stream().map(e -> e.getString("id")).collect(Collectors.toSet());
        for (Dnt dnt : data) {
            //组织
            qw = new Wrapper<>();
            qw.select("""
                    select org.id,org.name,org.pname,org.pid from sys_link_user_org lo
                    LEFT JOIN sys_org org ON org.id = lo.org_id
                    """);
            qw.eq("lo.user_id", dnt.getString("id"));
            dnt.set("org", dao.list(qw));

            //角色
            qw = new Wrapper<>();
            qw.select("""
                    select role.id,role.role_key,role.name from sys_link_user_role lr
                    LEFT JOIN sys_role role ON role.id = lr.role_id
                    """);
            qw.in("lr.user_id", dnt.getString("id"));
            dnt.set("role", dao.list(qw));

            //职位
            qw = new Wrapper<>();
            qw.select("""
                    select post.id,post.code,post.name  from sys_link_user_post lp
                    LEFT JOIN sys_post post ON post.id = lp.post_id
                    """);
            qw.in("lp.user_id", dnt.getString("id"));
            dnt.set("post", dao.list(qw));
        }

        if (param.isPage()) {
            dntPage.setData(data);
            return dntPage;
        } else {
            return data;
        }
    }

    //查用户信息
    @Override
    public Dnt userInfo(Object id) {
        List<Dnt> dnts = userInfo(Arrays.asList(id));
        if (ArrayKit.isNotBlank(dnts)) {
            return dnts.get(0);
        }
        return null;
    }

    //查用户信息
    @Override
    public List<Dnt> userInfo(List<Object> ids) {
        //查账号信息
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.in("sys_user.id", ids);
        String sql = " select * from sys_user left join sys_user_prop on sys_user.id=sys_user_prop.id ";
        List<Dnt> sys_user = dao.listOrigin(sql, wrapper);

        return sys_user;
    }

    //重置密码
    @Override
    public boolean resetPassword(Param<JSONObject> param) {
        JSONArray ids = param.getJSONArray("ids");

        //用户列表
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.eq("d_s", 0);
        wrapper.in("id", ids);
        wrapper.select("id", "account");
        List<Dnt> sys_user = dao.list("sys_user", wrapper);

        //循环重置密码
        ArrayList<Dnt> updateDnt = new ArrayList<>();
        for (Dnt user : sys_user) {
            String defaultPassword = defaultPassword(user.getString("account"));

            Dnt update = Dnt.createUpdate();
            update.set("id", user.getString("id"));
            update.set("password", defaultPassword);
            updateDnt.add(update);
        }

        //批量更新
        long count = dao.updateBatchById("sys_user", updateDnt);

        return true;
    }

    @Override
    public Dnt refreshToken(Param<JSONObject> param) {
        String userId = param.getString("userId");
        String refreshToken = param.getString("refreshToken");

        //查用账号信息
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.eq("d_s", 0);
        wrapper.eq("id", userId);
        wrapper.eq("refresh_token", refreshToken);
        Dnt sys_user = dao.selectOne("sys_user", wrapper);
        if (sys_user == null) {
            throw new AblError(410, "刷新Token失败，未查询到用户信息，请重新登录");
        }

        //更新登录信息
        refreshToken = MathKit.getMD5(System.currentTimeMillis() + "");
        Dnt update = Dnt.createUpdate();
        update.set("id", userId);
        update.set("refresh_token", refreshToken);
        dao.updateById("sys_user", update);

        //签发Token
        HashMap<Object, Object> tokenParam = new HashMap<>();
        tokenParam.put("userId", userId);
        tokenParam.put("roleIds", userRoleIds(userId));
        tokenParam.put("userAccount", sys_user.getString("account"));
        String token = JwtKit.createToken(tokenParam, Integer.parseInt(system.dictValue("login_time_out", "2")));

        //
        Dnt data = Dnt.create();
        data.set("token", token);
        data.set("refreshToken", refreshToken);

        return data;
    }

    @Override
    public Dnt loginUserInfo(Param<JSONObject> param) {
        Dnt userInfo = loginUserInfo();
        userInfo.remove("password");
        return userInfo;
    }

    @Override
    public List<Dnt> menu(Param<JSONObject> param) {
        JSONArray roleIds = loginUserRoleIds();

        //关联菜单ID
        Wrapper<Object> qW = new Wrapper<>();
        qW.select("menu_id");
        qW.in("role_id", roleIds);
        List<Object> menuIds = dao.listObj("sys_link_role_menu", qW);

        //查询菜单
        HashSet<Object> ids = new HashSet<>();
        if (ArrayKit.isNotBlank(menuIds)) {
            qW = new Wrapper<>();
            qW.select("ids");
            qW.eq("d_s", 0);
            qW.in("id", menuIds);
            List<Dnt> sys_menu = dao.list("sys_menu", qW);
            for (Dnt dnt : sys_menu) {
                ids.addAll(dnt.getJSONArray("ids"));
            }
        }
        //返回菜单树
        if (ArrayKit.isNotBlank(ids)) {
            qW = new Wrapper<>();
            qW.in("id", ids);
            qW.orderByAsc("sort");
            return dao.list("sys_menu", qW);
        }

        return new ArrayList<>();
    }

    public Dnt userProp(String id) {
        //查用户属性信息
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.eq("id", id);

        return dao.selectOne("sys_user_prop", wrapper);
    }

    //password= Md5(Md5(账号+明文密码)+盐)
    private String defaultPassword(String account) {
        //加盐
        String salt = system.dictValue("user_salt", "gK3fQmj2tmWk3ScaW1jBch6svMe1CatO");

        //字典表-默认密码
        String dictPassword = system.dictValue("user_default_password", "1234qwer");
        String ap = MathKit.getMD5(account + dictPassword);

        //返回密码
        return MathKit.getMD5(ap + salt);
    }

    @Async
    public void async() {
        System.out.println("执行异步任务");
    }

    //返回用户角色IDS
    public List<Object> userRoleIds(String userId) {
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.eq("user_id", userId);
        return dao.listObj(Table.sys_link_user_role, wrapper);
    }
}
